1
|
|
|
/** |
2
|
|
|
* [Siteapp] - multi-purpose frontend application |
3
|
|
|
* |
4
|
|
|
* Siteapp media-query detector |
5
|
|
|
* |
6
|
|
|
* @package [Siteapp] |
7
|
|
|
* @subpackage [Siteapp] core |
8
|
|
|
* @author Björn Bartels <[email protected]> |
9
|
|
|
* @link https://gitlab.bjoernbartels.earth/groups/themes |
10
|
|
|
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0 |
11
|
|
|
* @copyright copyright (c) 2016 Björn Bartels <[email protected]> |
12
|
|
|
* |
13
|
|
|
* @namespace Siteapp |
14
|
|
|
* @module Siteapp.MediaQuery |
15
|
|
|
*/ |
16
|
|
|
|
17
|
|
|
// Default set of media queries |
18
|
|
|
var defaultQueries = { |
19
|
|
|
'default' : 'only screen', |
20
|
|
|
landscape : 'only screen and (orientation: landscape)', |
21
|
|
|
portrait : 'only screen and (orientation: portrait)', |
22
|
|
|
retina : 'only screen and (-webkit-min-device-pixel-ratio: 2),' + |
23
|
|
|
'only screen and (min--moz-device-pixel-ratio: 2),' + |
24
|
|
|
'only screen and (-o-min-device-pixel-ratio: 2/1),' + |
25
|
|
|
'only screen and (min-device-pixel-ratio: 2),' + |
26
|
|
|
'only screen and (min-resolution: 192dpi),' + |
27
|
|
|
'only screen and (min-resolution: 2dppx)' |
28
|
|
|
}; |
29
|
|
|
|
30
|
|
|
var MediaQuery = { |
31
|
|
|
queries: [], |
32
|
|
|
current: '', |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* Checks if the screen is at least as wide as a breakpoint. |
36
|
|
|
* @function |
37
|
|
|
* @param {String} size - Name of the breakpoint to check. |
38
|
|
|
* @returns {Boolean} `true` if the breakpoint matches, `false` if it's smaller. |
39
|
|
|
*/ |
40
|
|
|
atLeast: function(size) { |
41
|
|
|
var query = this.get(size); |
42
|
|
|
|
43
|
|
|
if (query) { |
44
|
|
|
return window.matchMedia(query).matches; |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
return false; |
48
|
|
|
}, |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* Gets the media query of a breakpoint. |
52
|
|
|
* @function |
53
|
|
|
* @param {String} size - Name of the breakpoint to get. |
54
|
|
|
* @returns {String|null} - The media query of the breakpoint, or `null` if the breakpoint doesn't exist. |
55
|
|
|
*/ |
56
|
|
|
get: function(size) { |
57
|
|
|
for (var i in this.queries) { |
|
|
|
|
58
|
|
|
var query = this.queries[i]; |
59
|
|
|
if (size === query.name) return query.value; |
|
|
|
|
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
return null; |
63
|
|
|
}, |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* Initializes the media query helper, by extracting the breakpoint list from the CSS and activating the breakpoint watcher. |
67
|
|
|
* @function |
68
|
|
|
* @private |
69
|
|
|
*/ |
70
|
|
|
_init: function() { |
71
|
|
|
var self = this; |
72
|
|
|
var extractedStyles = $('.Siteapp-mq').css('font-family'); |
73
|
|
|
var namedQueries; |
74
|
|
|
|
75
|
|
|
namedQueries = parseStyleToObject(extractedStyles); |
76
|
|
|
|
77
|
|
|
for (var key in namedQueries) { |
|
|
|
|
78
|
|
|
self.queries.push({ |
79
|
|
|
name: key, |
80
|
|
|
value: 'only screen and (min-width: ' + namedQueries[key] + ')' |
81
|
|
|
}); |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
this.current = this._getCurrentSize(); |
85
|
|
|
|
86
|
|
|
this._watcher(); |
87
|
|
|
|
88
|
|
|
// Extend default queries |
89
|
|
|
// namedQueries = $.extend(defaultQueries, namedQueries); |
90
|
|
|
}, |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* Gets the current breakpoint name by testing every breakpoint and returning the last one to match (the biggest one). |
94
|
|
|
* @function |
95
|
|
|
* @private |
96
|
|
|
* @returns {String} Name of the current breakpoint. |
97
|
|
|
*/ |
98
|
|
|
_getCurrentSize: function() { |
99
|
|
|
var matched; |
100
|
|
|
|
101
|
|
|
for (var i in this.queries) { |
|
|
|
|
102
|
|
|
var query = this.queries[i]; |
103
|
|
|
|
104
|
|
|
if (window.matchMedia(query.value).matches) { |
105
|
|
|
matched = query; |
106
|
|
|
} |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
if(typeof matched === 'object') { |
|
|
|
|
110
|
|
|
return matched.name; |
111
|
|
|
} else { |
112
|
|
|
return matched; |
113
|
|
|
} |
114
|
|
|
}, |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* Activates the breakpoint watcher, which fires an event on the window whenever the breakpoint changes. |
118
|
|
|
* @function |
119
|
|
|
* @private |
120
|
|
|
*/ |
121
|
|
|
_watcher: function() { |
122
|
|
|
var _this = this; |
123
|
|
|
|
124
|
|
|
$(window).on('resize.'+MediaQuery._ns+'.mediaquery', function() { |
125
|
|
|
var newSize = _this._getCurrentSize(); |
126
|
|
|
|
127
|
|
|
if (newSize !== _this.current) { |
128
|
|
|
// Broadcast the media query change on the window |
129
|
|
|
$(window).trigger('changed.'+MediaQuery._ns+'.mediaquery', [newSize, _this.current]); |
130
|
|
|
|
131
|
|
|
// Change the current media query |
132
|
|
|
_this.current = newSize; |
133
|
|
|
} |
134
|
|
|
}); |
135
|
|
|
} |
136
|
|
|
}; |
137
|
|
|
|
138
|
|
|
//Siteapp.MediaQuery = MediaQuery; |
139
|
|
|
|
140
|
|
|
// matchMedia() polyfill - Test a CSS media type/query in JS. |
141
|
|
|
// Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas, David Knight. Dual MIT/BSD license |
142
|
|
|
window.matchMedia || (window.matchMedia = function() { |
143
|
|
|
'use strict'; |
144
|
|
|
|
145
|
|
|
// For browsers that support matchMedium api such as IE 9 and webkit |
146
|
|
|
var styleMedia = (window.styleMedia || window.media); |
147
|
|
|
|
148
|
|
|
// For those that don't support matchMedium |
149
|
|
|
if (!styleMedia) { |
150
|
|
|
var style = document.createElement('style'), |
151
|
|
|
script = document.getElementsByTagName('script')[0], |
152
|
|
|
info = null; |
153
|
|
|
|
154
|
|
|
style.type = 'text/css'; |
155
|
|
|
style.id = 'matchmediajs-test'; |
156
|
|
|
|
157
|
|
|
script.parentNode.insertBefore(style, script); |
158
|
|
|
|
159
|
|
|
// 'style.currentStyle' is used by IE <= 8 and 'window.getComputedStyle' for all other browsers |
160
|
|
|
info = ('getComputedStyle' in window) && window.getComputedStyle(style, null) || style.currentStyle; |
161
|
|
|
|
162
|
|
|
styleMedia = { |
163
|
|
|
matchMedium: function(media) { |
164
|
|
|
var text = '@media ' + media + '{ #matchmediajs-test { width: 1px; } }'; |
165
|
|
|
|
166
|
|
|
// 'style.styleSheet' is used by IE <= 8 and 'style.textContent' for all other browsers |
167
|
|
|
if (style.styleSheet) { |
168
|
|
|
style.styleSheet.cssText = text; |
169
|
|
|
} else { |
170
|
|
|
style.textContent = text; |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
// Test if media query is true or false |
174
|
|
|
return info.width === '1px'; |
175
|
|
|
} |
176
|
|
|
}; |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
return function(media) { |
180
|
|
|
return { |
181
|
|
|
matches: styleMedia.matchMedium(media || 'all'), |
182
|
|
|
media: media || 'all' |
183
|
|
|
}; |
184
|
|
|
}; |
185
|
|
|
}()); |
186
|
|
|
|
187
|
|
|
// Thank you: https://github.com/sindresorhus/query-string |
188
|
|
|
function parseStyleToObject(str) { |
189
|
|
|
var styleObject = {}; |
190
|
|
|
|
191
|
|
|
if (typeof str !== 'string') { |
192
|
|
|
return styleObject; |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
str = str.trim().slice(1, -1); // browsers re-quote string style values |
196
|
|
|
|
197
|
|
|
if (!str) { |
198
|
|
|
return styleObject; |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
styleObject = str.split('&').reduce(function(ret, param) { |
202
|
|
|
var parts = param.replace(/\+/g, ' ').split('='); |
203
|
|
|
var key = parts[0]; |
204
|
|
|
var val = parts[1]; |
205
|
|
|
key = decodeURIComponent(key); |
206
|
|
|
|
207
|
|
|
// missing `=` should be `null`: |
208
|
|
|
// http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters |
209
|
|
|
val = val === undefined ? null : decodeURIComponent(val); |
210
|
|
|
|
211
|
|
|
if (!ret.hasOwnProperty(key)) { |
212
|
|
|
ret[key] = val; |
213
|
|
|
} else if (Array.isArray(ret[key])) { |
214
|
|
|
ret[key].push(val); |
215
|
|
|
} else { |
216
|
|
|
ret[key] = [ret[key], val]; |
217
|
|
|
} |
218
|
|
|
return ret; |
219
|
|
|
}, {}); |
220
|
|
|
|
221
|
|
|
return styleObject; |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
export {MediaQuery}; |
225
|
|
|
|
226
|
|
|
|
When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically: